import { deploy } from '../src/deploy';
import { IParams } from '../src/params';
import { SaeInterface } from '../src/sae';
import * as sinon from 'sinon';
import { expect } from 'chai';
import * as step from '@flow-step/step-toolkit';
import * as tea from '@alicloud/tea-typescript'
import {
    DescribeApplicationConfigResponseBodyData,
    DeployApplicationRequest,
    DeployApplicationResponse
} from '@alicloud/sae20190506';


const params: IParams = {
    APPLICATION_ID: 'app123',
    Batch: '10',
    BatchWaitTime: '60',
    ReleaseType: 'manual',
    StrategyType: 'BatchUpdate',
    USE_ACR_EE: false,
    buildJobID: 0,
    domestic: false,
    pipelineID: 0,
    pipelineName: '',
    projectDir: '',
    ARTIFACT: 'artifact.jar',
    regionID: 'cn-hangzhou',
    workSpace: '',
    CHANGE_ORDER_ID: '12345',
    APP_NAME: 'my-app',
    PACKAGE: '{"ARTIFACT_URL": "test-url", "ARTIFACT_NAME": "artifact.jar", "WEB_DOWNLOAD_URL": "test", "artifact": "application.jar", "type": "flowPublic","downloadUrl": "test-url"}',
    GrayNumber: '10',
    minReadyInstances: '2',
    minReadyInstanceRatio: '50',
    minReadyInstanceType: 'byNumber',
    waitTimeInBatch: '30',
    enableGreyTagRoute: 'true'
};

describe('deployApplication', () => {
    let mockSae: SaeInterface;
    let sandbox: sinon.SinonSandbox;
    let deployApplicationStub: sinon.SinonStub;
    let addOutputStub: sinon.SinonStub;
    let runActionStub: sinon.SinonStub;
    let exportEnvVarStub: sinon.SinonStub;
    let infoStub: sinon.SinonStub;
    let appendActionsStub: sinon.SinonStub;
    let describeConfigStub: sinon.SinonStub;
    let generateDeployRequestStub: sinon.SinonStub;
    let sleepStub: sinon.SinonStub;
    let checkStub: sinon.SinonStub;
    let appendErrorMessageStub: sinon.SinonStub;

    const mockConfig = new DescribeApplicationConfigResponseBodyData({
        appName: 'my-app',
        packageType: 'JAR'
    })

    const mockRequest = new DeployApplicationRequest({
        appId: 'app123',
        PackageVersion: 'DATE',
        updateStrategy: JSON.stringify({
            type: 'BatchUpdate',
            batchUpdate: {
                batch: '10',
                releaseType: 'manual',
                batchWaitTime: '60'
            }
        }),
        enableGreyTagRoute: true,
        packageUrl: 'test-url',
        minReadyInstances: '2',
        batchWaitTime:'30',
    })

    beforeEach(() => {
        sandbox = sinon.createSandbox();
        deployApplicationStub = sandbox.stub();
        addOutputStub = sandbox.stub(step, 'addOutput');
        runActionStub = sandbox.stub(step.interaction, 'runAction');
        exportEnvVarStub = sandbox.stub(step, 'exportEnvVar');
        infoStub = sandbox.stub(step, 'info');
        appendActionsStub = sandbox.stub(step.interaction, 'appendActions');
        describeConfigStub = sandbox.stub(require('../src/deployUtils'), 'describeConfig');
        generateDeployRequestStub = sandbox.stub(require('../src/deployUtils'), 'generateDeployRequest');
        sleepStub = sandbox.stub(tea,'sleep');
        checkStub = sandbox.stub(require('../src/check'), 'check')
        appendErrorMessageStub = sandbox.stub(step.interaction, 'appendErrorMessage');


        mockSae = {
            deployApplication: deployApplicationStub
        } as unknown as SaeInterface;

        describeConfigStub.resolves(mockConfig)
        generateDeployRequestStub.returns(mockRequest)
    });

    afterEach(() => {
        sandbox.restore();
    });


    it('should handle successful deployment', async () => {

        const mockResponse = new DeployApplicationResponse({
            body: {
                code: '200',
                message: 'success'
            }
        });

        deployApplicationStub.resolves(mockResponse);
        sleepStub.resolves()
        checkStub.resolves()

        await deploy(params, mockSae);

        expect(deployApplicationStub.calledOnce).to.be.true;
        expect(deployApplicationStub.firstCall.args[0]).to.deep.equal(mockRequest);
        expect(infoStub.calledWith('CHECKING DEPLOYMENT STATUS')).to.be.true;
        expect(addOutputStub.calledWith('GLOBAL_CANCEL_HOOK_ACTION=TERMINATE')).to.be.true;
    });

    it('should handle failed deployment', async () => {
        const mockResponse = new DeployApplicationResponse({
            body: {
                code: '500',
                message: 'Service Error'
            }
        });

        deployApplicationStub.resolves(mockResponse);

        try {
            await deploy(params, mockSae);
            expect.fail('Expected deployApplication to throw an error');
        } catch (e: any) {
            expect(e.message).to.equal('failed to deploy application: Service Error');
        }
    });

    it('should handle deployment failure', async () => {
        const error = new Error('Some error occurred');
        deployApplicationStub.rejects(error);

        try {
            await deploy(params, mockSae);
            expect.fail('Expected deployApplication to throw an error');
        } catch (e: any) {
            expect(e.message).to.equal(`failed to deployApplication: ${error}`);
        }
    });


    it('should handle SAE in use deployment failure', async () => {
        const error = new Error('SAE is in progress');
        deployApplicationStub.rejects(error);

        try {
            await deploy(params, mockSae);
            expect.fail('Expected deployApplication to throw an error');
        } catch (e: any) {
            expect(appendErrorMessageStub.calledWith('SAE service is experiencing issues or there are ongoing deployment tasks. Please check the SAE console.')).to.be.true;
            expect(e.message).to.equal(`failed to deployApplication: Error: SAE is in progress`);
        }
    });

    it('should handle ImageUrl invalid failure', async () => {
        const error = new Error('The parameter is invalid {ImageUrl}');
        deployApplicationStub.rejects(error);

        try {
            await deploy(params, mockSae);
            expect.fail('Expected deployApplication to throw an error');
        } catch (e: any) {
            expect(appendErrorMessageStub.calledWith('SAE cannot access your imageUrl. Please check the SAE console.')).to.be.true;
            expect(e.message).to.equal(`failed to deployApplication: Error: The parameter is invalid {ImageUrl}`);
        }
    });

});
